﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using OpenTracing;

namespace Solution.Application.Behaviors
{
    public class LoggingBehavior<TInput, TOutput> : IPipelineBehavior<TInput, TOutput> where TInput : IRequest<TOutput>
    {
        private readonly ITracer _tracer;
        private readonly ILogger<LoggingBehavior<TInput, TOutput>> _logger;

        public LoggingBehavior(ILogger<LoggingBehavior<TInput, TOutput>> logger)
        {
            _logger = logger;
            _tracer = OpenTracing.Util.GlobalTracer.Instance;
        }

        public async Task<TOutput> Handle(TInput request, CancellationToken cancellationToken, RequestHandlerDelegate<TOutput> next)
        {
            Type requestType = request.GetType();
            using (IScope scope = _tracer.BuildSpan(requestType.FullName).StartActive(true))
            {
                WriteSpanLog(scope, request, requestType);
                try
                {
                    string requestName = typeof(TInput).Name;

                    _logger.LogInformation($"Request Json:{JsonConvert.SerializeObject(request)}");

                    var timer = new Stopwatch();
                    timer.Start();
                    var response = await next();
                    timer.Stop();

                    var responseJson = JsonConvert.SerializeObject(response);
                    _logger.LogInformation($"Response Json:{responseJson}");
                    scope.Span.Log(responseJson);

                    return response;
                }
                catch (Exception ex)
                {
                    //scope.Span.SetTag("error", true);
                    scope.Span.Log(new Dictionary<string, object>()
                    {
                        {"error",true },
                        {"msg",ex.Message }
                    });
                    throw;
                }
                finally
                {
                    scope.Span.Finish();
                }
            }
        }

        private void WriteSpanLog(IScope scope, TInput request, Type requestType)
        {
            var dict = new Dictionary<string, object>();
            foreach (PropertyInfo prop in requestType.GetProperties())
            {
                object propValue = prop.GetValue(request, null);
                if (propValue == null)
                {
                    continue;
                }

                dict.Add(prop.Name, propValue);

                //Set Tags for searching
                //scope.Span.SetTag(prop.Name, $"{propValue}");
            }

            scope.Span.Log(dict);
        }
    }
}